package com.geek.bams.service;


import com.geek.bams.dao.FileDao;
import com.geek.bams.dao.IDao;
import com.geek.bams.entity.*;
import com.geek.bams.exception.*;
import com.geek.bams.vo.RankVO;


import java.io.IOException;
import java.util.List;
import java.util.TreeSet;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Desc 业务逻辑层
 * @Author chenmin
 * @Date 2025/10/13
 */
public class Bank {

    //私有的 静态的  本类的实例
    private static Bank bank;
    //面向接口编程  可插拔式编程  实现代码解耦
    private IDao<Account , List<Account>> dao;
    private static final ReentrantLock lock =new ReentrantLock();

    //私有的 构造方法
    private Bank(){
        //dao = new ArrayDao();
        //dao = new ListDao();
        dao = new FileDao();
    }

    //公开的 静态的  获取本类实例的方法
    public static Bank getInstance(){
        if(bank==null){
            bank = new Bank();
        }
        return bank;
    }

    /**
     * 用户开户
     * @param password   账户密码
     * @param repassword 重复密码
     * @param name       真实姓名
     * @param personID   身份证号
     * @param email      邮箱
     * @param type       选择账户类型  项目需求规定账户类型：0 – 储蓄账户  1 – 信用账户 2 – 可贷款储蓄账户 3– 可贷款信用账户
     * @return  银行账户
     */
    public Account register(String password, String repassword, String name, String personID, String email, int type)
            throws LoginException, RegisterException, TypeException, IOException {
        Account acc = null;
        if(password==null || password.equals("") || repassword==null || repassword.equals("") ||
                name==null || name.equals("") || personID==null || personID.equals("")){
            /*System.out.println("账户信息不能为空！");
            return null;*/
            throw new LoginException("账户信息不能为空！");
        }

        if(password.equals(repassword)){
            //生成ID
            Long id = dao.createID();
            if( id<=0){
                /*System.out.println("ID生成有误");
                return null;*/
                throw new RegisterException("ID生成有误!");
            }

            //多态：父类的引用指向子类的实例
            switch (type){
                case 0:  //储蓄账户
                    acc = new SavingAccount(id , name , password , personID , email , 0.0);
                    break;
                case 1:  //信用账户
                    acc = new CreditAccount(id , name , password , personID , email , 0.0 , 1500.0);
                    break;
                case 2:  //可贷款储蓄账户
                    acc = new LoanSavingAccount(id ,name, password , personID , email ,0.0 , 0L);
                    break;
                case 3:  //可贷款信用账户
                    acc = new LoanCreditAccount(id ,name, password , personID , email ,0.0 , 1500.0 , 0L);
                    break;
                default:
                    //System.out.println("账户类型未匹配！");
                    throw new TypeException("账户类型未匹配!");
            }

            //注册账户信息
            if(acc!=null){
                boolean flag = dao.insert(acc);
                if(flag){
                    System.out.println("开户成功");
                }else {
                    //System.out.println("开户失败");
                    throw new RegisterException("开户失败!");
                }
            }
        }else{
            //System.out.println("两次密码输入不一致！");
            throw new RegisterException("两次密码输入不一致!");
        }
        return acc;
    }


    /**
     * 用户登录
     * @param id        账户ID
     * @param password  账户密码
     * @return
     */
    public Account login(Long id, String password) throws LoginException, IOException, ClassNotFoundException {
        if(id==null || password==null || password.equals("")){
            /*System.out.println("账户信息不能为空！");
            return null;*/
            throw new LoginException("账户信息不能为空！");
        }
        //查询
        Account acc = dao.selectOne(id, password);
        if(acc!=null){
            System.out.println("登录成功");
        }else{
            //System.out.println("登录失败");
            throw new LoginException("登录失败！");
        }
        return acc;
    }


    /**
     * 用户存款
     * @param id     账户ID
     * @param money  存的钱
     * @return
     */
    public Account deposit(Long id, double money) throws LoginException, IOException {
        lock.lock();
        try {
            if(id==null){
                /*System.out.println("账户信息不能为空！");
                return null;*/
                throw new LoginException("账户信息不能为空！");
            }
            //查询
            Account acc = dao.selectOne(id);
            if(acc!=null){
                acc.deposit(money);
                // 保存更新后的账户信息到文件
                dao.update(acc);
                System.out.println("存款成功，当前余额：" + acc.getBalance());
            }else{
                //System.out.println("账户不存在！");
                throw new LoginException("账户不存在！");
            }
            return acc;
        } catch (LoginException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }


    /**
     * 用户取款
     * @param id        账户ID
     * @param password  账户密码
     * @param money     去的钱
     * @return
     */
    public Account withdraw(Long id, String password, double money) throws BalanceNotEnoughException, LoginException, IOException, ClassNotFoundException {
        lock.lock();
        try {
            if(id==null || password==null || password.equals("")){
                /*System.out.println("账户信息不能为空！");
                return null;*/
                throw new LoginException("账户信息不能为空！");
            }

            //查询
            Account acc = dao.selectOne(id, password);
            if(acc!=null){
                // 对于信用账户，即使余额为0也可以使用透支额度，所以不在这里检查余额
                // 对于储蓄账户，withdraw方法内部会检查余额是否充足
                // 编译看左边，运行看右边
                acc.withdraw(money);
                // 保存更新后的账户信息到文件
                dao.update(acc);
                System.out.println("取款成功，当前余额：" + acc.getBalance());
            }else{
                //System.out.println("账户密码不匹配！");
                throw new LoginException("账户密码不匹配！");
            }
            return acc;
        } catch (LoginException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (BalanceNotEnoughException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }


    /**
     * 设置透支额度
     * @param id        账户ID
     * @param password  账户密码
     * @param money     修改的透支额度
     * @return
     */
    public Account updateCeiling(Long id, String password, double money) throws LoginException, TypeException, IOException, ClassNotFoundException {
        if(id==null || password==null || password.equals("")){
            /*System.out.println("账户信息不能为空！");
            return null;*/
            throw new LoginException("账户信息不能为空！");
        }
        //查询
        Account acc = dao.selectOne(id, password);
        if(acc!=null){
            //判断账户的运行期类型是否是CreditAccount,若是则可以设置透支额度
            if(acc instanceof CreditAccount){
                ((CreditAccount) acc).setCeiling(money);
                // 保存更新后的账户信息到文件
                dao.update(acc);
                System.out.println("透支额度修改成功，当前透支额度：" + ((CreditAccount) acc).getCeiling());
            }else{
                //System.out.println("当前账户不支持修改透支额度！");
                throw new TypeException("当前账户不支持修改透支额度！");
            }
        }else{
            //System.out.println("账户密码不匹配！");
            throw new LoginException("账户密码不匹配！");
        }
        return acc;
    }


    /**
     * 转账功能
     * @param from  转出账户
     * @param passwordFrom  转出账号的密码
     * @param to    转入账户
     * @param money 转账的金额
     * @return
     */
    public boolean transfer(Long from, String passwordFrom, Long to, double money)
            throws BalanceNotEnoughException, LoginException, TransferException, IOException, ClassNotFoundException {
        if(from==null || passwordFrom==null || passwordFrom.equals("") || to==null){
            /*System.out.println("转出或转入账户不能为空");
            return false;*/
            throw new LoginException("转出或转入账户不能为空！");
        }
        if(from.equals(to)){
            /*System.out.println("转出或转入账户不能是同一个");
            return false;*/
            throw new TransferException("转出或转入账户不能是同一个!");
        }
        Account accFrom = dao.selectOne(from, passwordFrom);
        if(accFrom!=null){
            Account accTo = dao.selectOne(to);
            if(accTo!=null){
                //转帐逻辑
                //一、转出账户余额充足
                //方式一
                /*double oldBalance = accFrom.getBalance();
                double newBalance = accFrom.withdraw(money).getBalance();
                if(oldBalance==newBalance){
                    System.out.println("转出账户余额不足");
                    return false;
                }*/

                //方式二
                //储蓄账户
                if((accFrom instanceof SavingAccount) && accFrom.getBalance()<money){
                    /*System.out.println("转出账户余额不足");
                    return false;*/
                    throw new BalanceNotEnoughException("转出账户余额不足");
                }
                //信用账户
                if((accFrom instanceof CreditAccount) && accFrom.getBalance() + ((CreditAccount) accFrom).getCeiling() < money){
                    /*System.out.println("转出账户余额不足");
                    return false;*/
                    throw new BalanceNotEnoughException("转出账户余额不足");
                }
                //二、转账
                accFrom.withdraw(money);
                accTo.deposit(money);
                // 保存更新后的账户信息到文件
                dao.update(accFrom);
                dao.update(accTo);
                System.out.println("转账成功");
                return true;
            }else{
                //System.out.println("转入账户不存在");
                throw new LoginException("转入账户不存在！");
            }
        }else{
            //System.out.println("转出账户不存在！");
            throw new LoginException("转出账户不存在！");
        }
        //return false;
    }


    /**
     * 贷  款
     * @param id     账户ID
     * @param money  贷款金额
     * @return
     */
    public Account requestLoan(Long id , double money) throws LoanException, LoginException, TypeException, IOException {
        if(id==null){
            /*System.out.println("账户信息不能为空！");
            return null;*/
            throw new LoginException("账户信息不能为空！");
        }
        Account acc = dao.selectOne(id);
        if(acc!=null){
            if(acc instanceof Loanable){
                ((Loanable) acc).requestLoan(money);
                // 保存更新后的账户信息到文件
                dao.update(acc);
                System.out.println("贷款成功，贷款金额：" + money);
            }else{
                //System.out.println("当前账户不支持贷款");
                throw new TypeException("当前账户不支持贷款!");
            }
        }else{
            //System.out.println("账户不存在！");
            throw new LoginException("账户不存在！");
        }
        return acc;
    }

    /**
     * 还贷款
     * @param id     账户ID
     * @param money  还款金额
     * @return
     */
    public Account payLoan(Long id , double money) throws LoanException, LoginException, TypeException, IOException {
        if(id==null){
            /*System.out.println("账户信息不能为空！");
            return null;*/
            throw new LoginException("账户信息不能为空！");
        }
        Account acc = dao.selectOne(id);
        if(acc!=null){
            if(acc instanceof Loanable){
                //多态  编译看左边，运行看右边
                ((Loanable) acc).payLoan(money);
                // 保存更新后的账户信息到文件
                dao.update(acc);
                System.out.println("还款成功，还款金额：" + money);
            }else{
                //System.out.println("当前账户不支持还贷");
                throw new TypeException("当前账户不支持还贷!");
            }
        }else{
            //System.out.println("账户不存在！");
            throw new LoginException("账户不存在！");
        }
        return acc;
    }

    /**
     * 统计银行所有账户余额总数
     * @return
     */
    public double totalBalance(){
        double sum = 0.0;
        for (Account acc : dao.selectAll()) {
            if(acc!=null){
                sum+=acc.getBalance();
            }
        }
        return sum;
    }

    /**
     * 统计所有信用账户透支额度总数
     * @return
     */
    public double totalCeiling(){
        double sum = 0.0;
        for (Account acc : dao.selectAll()) {
            if(acc!=null){
                if(acc instanceof CreditAccount){
                    sum+=((CreditAccount) acc).getCeiling();
                }
            }
        }
        return sum;
    }


    /**
     * 统计所有账户贷款的总额
     * @return
     */
    public double totalLoanAmount(){
        double sum = 0.0;
        for (Account acc : dao.selectAll()) {
            if(acc!=null){
                if(acc instanceof LoanSavingAccount){
                    sum += ((LoanSavingAccount) acc).getLoanAmount();
                }
                if(acc instanceof LoanCreditAccount){
                    sum += ((LoanCreditAccount) acc).getLoanAmount();
                }
            }
        }
        return sum;
    }


    /**
     * 所有用户的总资产排名
     */
    public TreeSet<RankVO> rank(){
        TreeSet<RankVO> set = new TreeSet<>();
        for (Account acc : dao.selectAll()) {
            if(acc!=null){
                String personId = acc.getPersonId();
                String name = acc.getName();
                double total = totalBalanceByPersonId(personId);
                //组装RankVO对象
                RankVO rankVO = new RankVO(personId , name , total);
                set.add(rankVO);
            }
        }
        return set;
    }

    //统计传入的身份证号名下总资产
    private double totalBalanceByPersonId(String personId){
        double sum = 0.0;
        for (Account acc : dao.selectAll()) {
            if(acc!=null && acc.getPersonId().equals(personId)){
                sum+=acc.getBalance();
            }
        }
        return sum;
    }


}
